package com.mf.storage.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.mf.storage.property.StorageProperties;
import com.mf.storage.service.ObjectStorageService;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.region.Region;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 阿里OSS对象存储实现类
 * User: zhaoming
 * Date: 2019-02-13
 * To change this template use File | Settings | File Templates.
 **/
public class OssObjectStorageServiceImpl implements ObjectStorageService {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private StorageProperties storageProperties;

    public OssObjectStorageServiceImpl(StorageProperties storageProperties){
        this.storageProperties = storageProperties;
    }

    public OSSClient getOssClient(){
        return new OSSClient(MessageFormat.format("http://{0}.aliyuncs.com", storageProperties.getRegion()), storageProperties.getAccessKey(), storageProperties.getSecretKey());
    }

    /**
     * 上传文件对象
     * @param bucketName 存储名称
     * @param objectName 对象名称
     * @param file 文件
     */
    public void putObject(String bucketName, String objectName, File file){
        logger.info("方法：上传文件对象");
        logger.info("参数：桶名: {}, 键: {}", bucketName, objectName);
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            PutObjectResult putObjectResult = ossClient.putObject(bucketName, objectName, file);
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms，返回参数：{}", endTime - startTime, JSONObject.toJSONString(putObjectResult));
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 上传输入流对象
     * @param bucketName 存储名称
     * @param objectName 对象名称
     * @param in 输入流
     */
    public void putObject(String bucketName, String objectName, InputStream in){
        logger.info("方法：上传输入流对象");
        logger.info("参数：桶名: {}, 键: {}", bucketName, objectName);
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            PutObjectResult putObjectResult = ossClient.putObject(bucketName, objectName, in);
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms，返回参数：{}", endTime - startTime, JSONObject.toJSONString(putObjectResult));
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 下载文件到输入流
     * @param bucketName 存储名称
     * @param objectName 对象名称
     * @return 字节数组
     */
    public byte[] getObject(String bucketName, String objectName){
        logger.info("方法：下载文件到输入流");
        logger.info("参数：桶名: {}, 键: {}", bucketName, objectName);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            OSSObject ossObject = ossClient.getObject(bucketName, objectName);
            InputStream in = ossObject.getObjectContent();
            byte[] buffer = new byte[1024];
            int len;
            while((len = in.read(buffer))!=-1){
                out.write(buffer, 0, len);
            }
            in.close();
            out.close();
            ossObject.close();
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms，返回参数：{}", endTime - startTime);
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException | IOException e){
            throw new RuntimeException(e.getMessage(), e);
        }
        return out.toByteArray();
    }

    /**
     * 下载文件到本地
     * @param bucketName 存储名称
     * @param objectName 对象名称
     * @param file 文件
     */
    public void getObject(String bucketName, String objectName, File file){
        logger.info("方法：下载文件到本地");
        logger.info("参数：桶名: {}, 键: {}", bucketName, objectName);
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            // 下载OSS文件到本地文件。如果指定的本地文件存在会覆盖，不存在则新建。
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, objectName);
            ossClient.getObject(getObjectRequest, file);
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms", endTime - startTime);
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 删除单个对象
     * @param bucketName 存储名称
     * @param objectName 对象名称
     */
    public void deleteObject(String bucketName, String objectName){
        logger.info("方法：删除单个文件");
        logger.info("参数：桶名: {}, 键: {}", bucketName, objectName);
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            ossClient.deleteObject(bucketName, objectName);
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms", endTime - startTime);
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 删除多个对象
     * @param bucketName 存储名称
     * @param objectNames 对象名称列表
     * @return 执行结果
     */
    public Map<String, List<String>> deleteObjects(String bucketName, List<String> objectNames){
        logger.info("方法：删除多个文件");
        logger.info("参数：桶名: {}, 键: {}", bucketName, JSONObject.toJSONString(objectNames));
        Map<String, List<String>> map = new HashMap<>();
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(objectNames));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            map.put("success", deletedObjects);
            map.put("error", new ArrayList<>());
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms", endTime - startTime);
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
        return map;
    }

    /**
     * 获取对象URL
     * @param bucketName 存储名称
     * @param objectName 对象名称
     * @return 对象访问URL
     */
    public String getObjectUrl(String bucketName, String objectName){
        return MessageFormat.format("https://{0}.{1}.aliyuncs.com/{2}", bucketName, storageProperties.getRegion(), objectName);
    }

    /**
     * 获取对象列表
     * @param bucketName 存储名称
     * @param prefix 前缀
     * @param marker 标记
     * @param delimiter 对象名称分组
     * @param maxKeys 最大个数（默认值为1000，最大值为1000）
     * @return 对象列表
     */
    public List<String> listObjects(String bucketName, String prefix, String marker, String delimiter, Integer maxKeys){
        logger.info("方法：获取对象列表");
        logger.info("参数：bucketName: {}, prefix: {}, marker: {}, delimiter: {}, maxKeys: {}", bucketName, prefix, marker, delimiter, maxKeys);
        List<String> keys;
        try{
            long startTime = System.currentTimeMillis();
            OSSClient ossClient = getOssClient();
            ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                    .withPrefix(prefix)
                    .withMaxKeys(maxKeys)
                    .withDelimiter(delimiter)
                    .withMarker(marker);
            ObjectListing objectListing = ossClient.listObjects(listObjectsRequest);
            List<OSSObjectSummary> ossObjectSummaries = objectListing.getObjectSummaries();
            keys = ossObjectSummaries.stream().map(OSSObjectSummary::getKey).collect(Collectors.toList());
            ossClient.shutdown();
            long endTime = System.currentTimeMillis();
            logger.info("执行成功，用时：{}ms", endTime - startTime);
        } catch (OSSException e){
            throw new RuntimeException(e.getErrorMessage(), e);
        } catch (ClientException e){
            throw new RuntimeException(e.getMessage(), e);
        }
        return keys;
    }

    public static void main(String[] args) throws IOException {
        StorageProperties storageProperties = new StorageProperties();
        storageProperties.setType("OSS");
        storageProperties.setRegion("oss-cn-beijing");
        storageProperties.setAccessKey("LTAIz02xXK75BWKl");
        storageProperties.setSecretKey("JYX9WKORBjWzPZSzuKVXf1p9F5a16Z");
        String bucketName = "photos-1256650050";
        String key = "20181016171401.jpg";
        File file = new File("c://20181016171401.jpg");
        Date expiration = new Date(new Date().getTime() + 6 * 3600 * 1000);
        OssObjectStorageServiceImpl ossObjectService = new OssObjectStorageServiceImpl(storageProperties);
        System.out.println(ossObjectService.getObjectUrl(bucketName, key));
    }
}
